package com.mpdbailey.cleverdicandroid;

import java.util.ArrayList;
import java.util.List;
import java.util.Locale;
import java.util.regex.Pattern;

import com.pigdogbay.androidutils.utils.StringUtils;

public class WordList
{
	private int _ResultLimit = 200;
	public final static int MAX_WORD_LEN = 30;
	List<String> _WordList;

	/*
	 * Wordlist must be sorted and all lower case
	 */
	public void SetWordList(List<String> wordList)
	{
		_WordList = wordList;
	}

	public WordList()
	{
		_WordList = null;
	}

	public void SetResultLimit(int limit)
	{
		_ResultLimit = limit;
	}

	public void RunQuery(String query, WordListCallback callback)
	{
		if (query.contains("#"))
		{
			FindWildcardWords(query,callback);
		}
		else if (IsPartialWord(query))
		{
			FindPartialWords(query,callback);
		}
		else if (query.contains(" "))
		{
			String[] words = query.split(" ");
			FindMultiwordAnagrams(words[0], words[1],callback);
		}
		else FindAnagrams(query,callback);
	}

	public void FindAnagrams(String anagram, WordListCallback callback)
	{
		anagram = StringUtils.WordSort(anagram);
		int length = anagram.length();
		for (String word : _WordList)
		{
			if (word.length() == length)
			{
				if (anagram.equals(StringUtils.WordSort(word)))
				{
					callback.Update(word);
				}
			}
		}
	}

	public void FindSubAnagrams(String anagram, WordListCallback callback)
	{
		ArrayList<String> matches = new ArrayList<String>();
		List<String> subWords = StringUtils.GetSubWords(anagram);
		int length = anagram.length()-1;
		ArrayList<String> reducedWordList = new ArrayList<String>();
		for (String word : _WordList)
		{
			if (word.length()==length)
			{
				reducedWordList.add(word);
			}
		}
		for (String word : subWords)
		{
			String subAnagram = StringUtils.WordSort(word);
			char missingLetter = StringUtils.GetMissingLetter(anagram,word);
			for (String match : reducedWordList)
			{
				if (!matches.contains(match) &&
						subAnagram.equals(StringUtils.WordSort(match)))
				{
					matches.add(match);
					callback.Update(match+" ("+missingLetter+")");
				}
			}
		}
	}

	public void FindPartialWords(String partialWord, WordListCallback callback)
	{
		int count = 0;
		int length = partialWord.length();
		Pattern pattern = CreatePattern(partialWord);
		for (String word : _WordList)
		{
			if (word.length() == length)
			{
				if (pattern.matcher(word).matches())
				{
					callback.Update(word);
					count++;
					if (count == _ResultLimit)
					{
						break;
					}
				}
			}
		}
	}
	public void FindWildcardWords(String wildcard, WordListCallback callback)
	{
		int count = 0;
		Pattern pattern = CreatePattern(wildcard);
		for (String word : _WordList)
		{
			if (pattern.matcher(word).matches())
			{
				callback.Update(word);
				count++;
				if ( _ResultLimit==count)
				{
					break;
				}
			}
		}
	}

	public boolean IsPartialWord(String s)
	{
		return s.contains(".");
	}

	public String ConvertQuery(String query)
	{
		// Convert numbers to that many dots
		query = query.replace("2", "..").replace("3", "...")
				.replace("4", "....").replace("5", ".....")
				.replace("6", "......").replace("7", ".......")
				.replace("8", "........").replace("9", ".........");

		if (query.length() > MAX_WORD_LEN)
		{
			query = query.substring(0, MAX_WORD_LEN);
		}

		char[] chars = query.toLowerCase(Locale.US).toCharArray();
		for (int i = 0; i < chars.length; i++)
		{
			if ((chars[i] < 'a' || chars[i] > 'z') && chars[i] != ' '
					&& chars[i] != '#')
			{
				chars[i] = '.';
			}
		}
		return new String(chars);
	}

	private Pattern CreatePattern(String s)
	{
		s = s.toLowerCase(Locale.US);
		s = s.replace(".", "[a-z]");
		s = s.replace("#", "[a-z]+");
		return Pattern.compile(s, Pattern.CASE_INSENSITIVE);
	}

	public List<String> GetMatchingWords(char[] bannedLetters, int size)
	{
		ArrayList<String> matches = new ArrayList<String>();
		for (String word : _WordList)
		{
			if (word.length() == size
					&& StringUtils.DoesNotContainBannedLetters(word,
							bannedLetters))
			{
				matches.add(word);
			}
		}
		return matches;
	}

	// / <summary>
	// / Find which letters are not used
	// / Remove all words which are not correct size
	// / Remove all words which contain any of the unused letters
	// / For each word in the wordlist1, find the unused letters
	// / See if any word in wordlist2 matches the unused letters
	// / </summary>
	public void FindMultiwordAnagrams(String word1, String word2, WordListCallback callback)
	{
		int count = 0;
		String sortedLetters = StringUtils.WordSort(word1 + word2);
		char[] bannedLetters = StringUtils.SubtractChars(
				StringUtils.LOWERCASE_ALPHABET, sortedLetters).toCharArray();
		List<String> list1 = GetMatchingWords(bannedLetters, word1.length());
		List<String> list2 = word1.length() == word2.length() ? list1
				: GetMatchingWords(bannedLetters, word2.length());
		List<String> sortedList2 = SortEachWord(list2);
		for (String word : list1)
		{
			String remainder = StringUtils.SubtractChars(sortedLetters, word);
			for (int i = 0; i < sortedList2.size(); i++)
			{
				if (sortedList2.get(i).equals(remainder))
				{
					callback.Update(word + " " + list2.get(i));
					count++;
					if (count == _ResultLimit)
					{
						return;
					}
				}
			}
		}
	}

	private List<String> SortEachWord(List<String> unsortedWordsList)
	{
		ArrayList<String> sortedList = new ArrayList<String>();
		for (String word : unsortedWordsList)
		{
			sortedList.add(StringUtils.WordSort(word));
		}
		return sortedList;
	}
}

